/*
 *
 *    Copyright (C) 2011 Freddy Barrera All Rights Reserved.
 *
 *    This program is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU General Public License
 *    as published by the Free Software Foundation; either version 3
 *    of the License, or Mozilla Public License any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 *    USA.
 *
 *    Redistribution and use in source and binary forms, with or without
 *    modification, are permitted provided that the following conditions are
 *    met:
 *
 *    -Redistribution of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *
 *    -Redistribution in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *
 *    Neither the name of Freddy Barrera or the names of contributors may
 *    be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 *    This software is provided "AS IS," without a warranty of any kind. ALL
 *    EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 *    ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 *    OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. Freddy Barrera
 *    AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 *    AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 *    DERIVATIVES. IN NO EVENT WILL Freddy Barrera OR ITS LICENSORS BE LIABLE
 *    FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 *    CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 *    REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 *    INABILITY TO USE THIS SOFTWARE, EVEN IF Freddy HAS BEEN ADVISED OF THE
 *    POSSIBILITY OF SUCH DAMAGES.
 *
 *    You acknowledge that this software is not designed, licensed or intended
 *    for use in the design, construction, operation or maintenance of any
 *    nuclear facility.
 *
 *
 */

package mx.uacam.ermilo.ercu.data.dao.hibernate;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import mx.uacam.ermilo.ercu.data.dao.interfaces.GenericDAO;
import mx.uacam.ermilo.ercu.data.exceptions.DuplicateEntryException;
import mx.uacam.ermilo.ercu.data.exceptions.EntryNotFoundException;
import org.hibernate.Session;
import org.hibernate.LockMode;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.Criteria;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;

/**
 * This abtract class define the basics methods to add, save and delete elements
 * of the data base using hibernate.
 * @param <T> the Class entity.
 * @param <ID> the Class type of the key into database.
 *
 * @author Freddy Barrera
 */
public abstract class GenericHibernateDAO<T, ID extends Serializable>
    implements GenericDAO<T, ID> {

    /** The class persistent to work.*/
    private Class<T> persistentClass;
    /** The hibernate session.*/
    private Session session;

    /**
     * This initialize the persistent Class.
     */
    public GenericHibernateDAO() {
        this.persistentClass = (Class<T>)
                ((ParameterizedType) getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }

    /**
     * This method return a hibernate sesion if is active otherwise throw
     * a exception.
     * @return a hibernate session.
     * @throws IllegalStateException
     */
    public final Session getSession() {
        if (session == null) {
            throw new IllegalStateException("Session has not been set on"
                    + " DAO before usage");
        }
        return session;
    }

    /**
     * This method set a <code>Session</code>.
     * @param aSession the hibernate session.
     */
    public final void setSession(final Session aSession) {
        session = aSession;
    }

    /**
     * This method get the persistent class.
     * @return a Class Persistent.
     */
    public final Class<T> getPersistentClass() {
        return persistentClass;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public final T findById(final ID id, final boolean lock)
            throws EntryNotFoundException {
        T entity;
        try {
            if (lock) {
                entity = (T) getSession().load(getPersistentClass(), id,
                        LockMode.UPGRADE);
            } else {
                entity = (T) getSession().load(getPersistentClass(), id);
            }
        } catch (ObjectNotFoundException ex) {
            throw new EntryNotFoundException(ex.getMessage());
        }

        return entity;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final List<T> findAll() {
        return findByCriteria();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public final List<T> findByExample(final T exampleInstance,
            final String[] excludeProperty) {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        Example example = Example.create(exampleInstance);
        for (String exclude : excludeProperty) {
            example.excludeProperty(exclude);
        }
        crit.add(example);
        return crit.list();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final T makePersistent(final T entity)
            throws DuplicateEntryException {
        Transaction transaction = getSession().beginTransaction();
        try {
            getSession().saveOrUpdate(entity);
            transaction.commit();
        } catch (org.hibernate.NonUniqueObjectException ex) {
            throw new DuplicateEntryException(ex.getMessage());
        } catch (org.hibernate.exception.ConstraintViolationException ex) {
            throw new DuplicateEntryException(ex.getMessage());
        }

        return entity;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void makeTransient(final T entity) {
        Transaction transaction = getSession().beginTransaction();
        getSession().delete(entity);
        transaction.commit();
    }

    /**
     * Force this session to flush.
     */
    public final void flush() {
        getSession().flush();
    }

    /**
     * This method clear the session.
     */
    public final void clear() {
        getSession().clear();
    }

    /**
     * Use this inside subclasses as a convenience method.
     * @param criterion the crtiterion to find.
     * @return a colleccion result.
     */
    @SuppressWarnings("unchecked")
    protected final List<T> findByCriteria(final Criterion... criterion) {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        for (Criterion c : criterion) {
            crit.add(c);
        }
        return crit.list();
    }
}